package com.gcloud.controller.network.service.impl;

import com.gcloud.common.util.StringUtils;
import com.gcloud.controller.ResourceProviders;
import com.gcloud.controller.network.dao.QosBandwidthLimitRuleDao;
import com.gcloud.controller.network.dao.QosFipPolicyBindingDao;
import com.gcloud.controller.network.dao.QosPolicyDao;
import com.gcloud.controller.network.dao.QosPortPolicyBindingDao;
import com.gcloud.controller.network.entity.QosBandwidthLimitRule;
import com.gcloud.controller.network.entity.QosPolicy;
import com.gcloud.controller.network.provider.IQosProvider;
import com.gcloud.controller.network.service.IQosBandwidthLimitRuleService;
import com.gcloud.controller.network.service.IQosPolicyService;
import com.gcloud.controller.network.util.QosUtil;
import com.gcloud.core.exception.GCloudException;
import com.gcloud.core.simpleflow.Flow;
import com.gcloud.core.simpleflow.FlowDoneHandler;
import com.gcloud.core.simpleflow.NoRollbackFlow;
import com.gcloud.core.simpleflow.SimpleFlowChain;
import com.gcloud.header.compute.enums.QosDirection;
import com.gcloud.header.enums.ResourceType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * Created by yaowj on 2018/10/30.
 */
@Slf4j
@Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class QosPolicyServiceImpl implements IQosPolicyService {

    @Autowired
    private IQosBandwidthLimitRuleService qosBandwidthLimitRuleService;

    @Autowired
    private QosPolicyDao qosPolicyDao;

    @Autowired
    private QosBandwidthLimitRuleDao qosBandwidthLimitRuleDao;

    @Autowired
    private QosPortPolicyBindingDao qosPortPolicyBindingDao;

    @Autowired
    private QosFipPolicyBindingDao qosFipPolicyBindingDao;

    @Override
    public QosPolicy create(Integer provider, String name, String description, Boolean isDefault, Boolean shared) {

        IQosProvider qosProvider = getProviderOrDefault(provider);
        String qosId = UUID.randomUUID().toString();
        QosPolicy policy = new QosPolicy();

        SimpleFlowChain<QosPolicy, String> chain = new SimpleFlowChain<>("create qos policy");
        chain.data(policy);

        chain.then(new NoRollbackFlow<QosPolicy>("create neutron qos policy") {
            @Override
            public void run(SimpleFlowChain chain, QosPolicy data) {
                String refQosId = qosProvider.createQosPolicy(name, description, isDefault, shared);
                data.setProviderRefId(refQosId);
                policy.setProvider(qosProvider.providerType().getValue());
                chain.next();
            }
            @Override
            public void rollback(SimpleFlowChain chain, QosPolicy data) {
                qosProvider.deleteQosPolicy(data.getProviderRefId());
                chain.rollback();
            }
        }).then(new NoRollbackFlow<QosPolicy>("save qos policy to db") {

            @Override
            public void run(SimpleFlowChain chain, QosPolicy data) {
                data.setId(qosId);
                qosPolicyDao.save(data);
                chain.next();
            }


        }).done(new FlowDoneHandler<org.openstack4j.model.network.QosPolicy>() {
            @Override
            public void handle(org.openstack4j.model.network.QosPolicy data) {
                chain.setResult(data.getId());
            }
        }).start();

        if(StringUtils.isNotBlank(chain.getErrorCode())){
            throw new GCloudException(chain.getErrorCode());
        }

        return policy;
    }

    public void delete(String id){

        QosPolicy qosPolicy = qosPolicyDao.getById(id);
        if(qosPolicy != null){
            //TODO 如果增加其他rule，要增加删除
            qosBandwidthLimitRuleDao.deleteByPolicyId(id);
            qosPortPolicyBindingDao.deleteByPolicyId(id);
            qosFipPolicyBindingDao.deleteByPolicyId(id);
            qosPolicyDao.deleteById(id);

            IQosProvider qosProvider = checkAndGetProvider(qosPolicy.getProvider());
            qosProvider.deleteQosPolicy(qosPolicy.getProviderRefId());
        }
    }

    @Override
    public void updateQosLimit(String policyId, Integer egress, Integer ingress){

        QosPolicy qosPolicy = qosPolicyDao.getById(policyId);
        if(qosPolicy == null){
            throw new GCloudException("::policy 不存在");
        }

        List<QosBandwidthLimitRule> bandwidthLimitRuleList = qosBandwidthLimitRuleDao.findByProperty(QosBandwidthLimitRule.QOS_POLICY_ID, policyId);

        Map<String, Object> ruleMap = new HashMap<>();

        if(bandwidthLimitRuleList != null && bandwidthLimitRuleList.size() > 0){
            for(QosBandwidthLimitRule rule : bandwidthLimitRuleList){
                if(QosDirection.EGRESS.value().equals(rule.getDirection())){
                    ruleMap.put("egressRule", rule);
                }else if(QosDirection.INGRESS.value().equals(rule.getDirection())){
                    ruleMap.put("ingressRule", rule);
                }
            }
        }

        //TODO 简化流程，测试限制设成0是否可以达到不限制的效果
        SimpleFlowChain<Map<String, Object>, String> chain = new SimpleFlowChain<>("update qos limit");
        chain.data(ruleMap).then(new Flow<Map<String, Object>>("update egress rule") {
            @Override
            public void run(SimpleFlowChain chain, Map<String, Object> data) {

                if(egress != null){
                    QosBandwidthLimitRule egressRule = data.get("egressRule") == null ? null : (QosBandwidthLimitRule)data.get("egressRule");
                    if(egressRule == null){
                        if(egress > 0){
                            QosBandwidthLimitRule newEgressRule = qosBandwidthLimitRuleService.create(qosPolicy, egress, QosUtil.burst(egress), QosDirection.EGRESS);
                            data.put("newEgressRule", newEgressRule);
                        }
                    }else{
                        if(egress > 0){
                            qosBandwidthLimitRuleService.update(qosPolicy, egressRule, egress, QosUtil.burst(egress), QosDirection.EGRESS);
                        }else{
                            qosBandwidthLimitRuleService.deleteQosBandwidthLimitRule(qosPolicy, egressRule);
                        }

                    }
                }
                chain.next();
            }

            @Override
            public void rollback(SimpleFlowChain chain, Map<String, Object> data) {
                if(egress != null){
                    QosBandwidthLimitRule egressRule = data.get("egressRule") == null ? null : (QosBandwidthLimitRule)data.get("egressRule");
                    if(egressRule == null){
                        QosBandwidthLimitRule newEgressRule = (QosBandwidthLimitRule)data.get("newEgressRule");
                        qosBandwidthLimitRuleService.deleteQosBandwidthLimitRule(qosPolicy, newEgressRule);
                    }else{
                        // egress小于0时，会把规则删除
                        if(egress < 0){
                            qosBandwidthLimitRuleService.create(qosPolicy, egressRule.getMaxKbps(), QosUtil.burst(egressRule.getMaxKbps()), QosDirection.EGRESS);
                        }else{
                            qosBandwidthLimitRuleService.update(qosPolicy, egressRule, egressRule.getMaxKbps(), QosUtil.burst(egressRule.getMaxKbps()), QosDirection.EGRESS);
                        }

                    }
                }
                chain.rollback();
            }
        }).then(new Flow<Map<String, Object>>("update egress rule") {
            @Override
            public void run(SimpleFlowChain chain, Map<String, Object> data) {

                if(ingress != null){
                    QosBandwidthLimitRule ingressRule = data.get("ingressRule") == null ? null : (QosBandwidthLimitRule)data.get("ingressRule");
                    if(ingressRule == null){
                        QosBandwidthLimitRule newIngressRule = qosBandwidthLimitRuleService.create(qosPolicy, ingress, QosUtil.burst(ingress), QosDirection.INGRESS);
                        data.put("newIngressRule", newIngressRule);
                    }else{

                        if(ingress > 0){
                            qosBandwidthLimitRuleService.update(qosPolicy, ingressRule, ingress, QosUtil.burst(ingress), QosDirection.INGRESS);
                        }else{
                            qosBandwidthLimitRuleService.deleteQosBandwidthLimitRule(qosPolicy, ingressRule);
                        }

                    }
                }
                chain.next();
            }

            @Override
            public void rollback(SimpleFlowChain chain, Map<String, Object> data) {

                if(ingress != null) {
                    QosBandwidthLimitRule ingressRule = data.get("ingressRule") == null ? null : (QosBandwidthLimitRule) data.get("ingressRule");
                    if (ingressRule == null) {
                        QosBandwidthLimitRule newIngressRule = (QosBandwidthLimitRule) data.get("newIngressRuleId");
                        qosBandwidthLimitRuleService.deleteQosBandwidthLimitRule(qosPolicy, newIngressRule);
                    } else {
                        // egress小于0时，会把规则删除
                        if(ingress < 0){
                            qosBandwidthLimitRuleService.create(qosPolicy, ingressRule.getMaxKbps(), QosUtil.burst(ingressRule.getMaxKbps()), QosDirection.INGRESS);
                        }else{
                            qosBandwidthLimitRuleService.update(qosPolicy, ingressRule, ingressRule.getMaxKbps(), QosUtil.burst(ingressRule.getMaxKbps()), QosDirection.INGRESS);
                        }

                    }

                }
                chain.rollback();
            }
        }).start();

        if(StringUtils.isNotBlank(chain.getErrorCode())){
            throw new GCloudException(chain.getErrorCode());
        }

    }


    public QosPolicy createQosLimit(Integer provider, Integer egress, Integer ingress){

        SimpleFlowChain<QosPolicy, String> chain = new SimpleFlowChain<>("create qos limit");
        chain.then(new Flow<QosPolicy>("create qos policy") {
            @Override
            public void run(SimpleFlowChain chain, QosPolicy data) {
                QosPolicy qosPolicy = create(provider, "带宽限制", null, null, null);
                chain.data(qosPolicy);
                chain.next();
            }
            @Override
            public void rollback(SimpleFlowChain chain, QosPolicy data) {
                delete(data.getId());
                chain.rollback();
            }

            //创建规则的时候不会滚，直接又会policy回滚是删除
        }).then(new NoRollbackFlow<QosPolicy>("create egress") {
            @Override
            public void run(SimpleFlowChain chain, QosPolicy data) {
                if(egress != null && egress > 0){
                    qosBandwidthLimitRuleService.create(data, egress, QosUtil.burst(egress), QosDirection.EGRESS);
                }
                chain.next();
            }

        }).then(new NoRollbackFlow<QosPolicy>("create ingress") {
            @Override
            public void run(SimpleFlowChain chain, QosPolicy data) {
                if(ingress != null && ingress > 0){
                    qosBandwidthLimitRuleService.create(data, ingress, QosUtil.burst(ingress), QosDirection.INGRESS);
                }
                chain.next();
            }
        }).start();

        if(StringUtils.isNotBlank(chain.getErrorCode())){
            throw new GCloudException(chain.getErrorCode());
        }

        return chain.data();
    }


    private IQosProvider getProviderOrDefault(Integer providerType) {
        IQosProvider provider = ResourceProviders.getOrDefault(ResourceType.QOS, providerType);
        return provider;
    }

    private IQosProvider checkAndGetProvider(Integer providerType) {
        IQosProvider provider = ResourceProviders.checkAndGet(ResourceType.QOS, providerType);
        return provider;
    }

}
